use tetris_lib::{constant::*};
use crate::{model::TetrisModel};
//use std::fs::File;
//use std::io::Write;
use tge::{
    context::Context,
    event::{event_check, event_register, timer_exdata, timer_stage},
    game::{Model, Render},
    render::sprite::{Sprites, Sprite},
    render::style::{Color, Style},
    render::terminal::Term,
};
#[cfg(feature = "sdl")]
use tge::render::adapter::sdl::sdlsym;

pub struct TetrisRender {
    pub term: Term,
    pub main_scene: Sprites,
}

impl TetrisRender {
    pub fn new() -> Self {
        let t = Term::new();
        let mut s = Sprites::new("main");

        #[cfg(feature = "sdl")]
        let mut tsback = Sprite::new(0, 0, 35, 24);
        #[cfg(not(feature = "sdl"))]
        let mut tsback = Sprite::new(0, 0, 80, 30);
        #[cfg(feature = "sdl")]
        tsback.load_sdl("assets/tetris/back.sdl", 0, 0);
        #[cfg(not(feature = "sdl"))]
        tsback.load_esc("assets/tetris/back.txt", 0, 0);
        s.add_by_tag(tsback, "back");

        #[cfg(not(feature = "sdl"))]
        let l0 = Sprite::new(2, 7, HENG * 2, ZONG);
        #[cfg(feature = "sdl")]
        let l0 = Sprite::new(1, 2, HENG, ZONG);
        s.add_by_tag(l0, "grid0");

        #[cfg(not(feature = "sdl"))]
        let l1 = Sprite::new(55, 7, HENG * 2, ZONG);
        #[cfg(feature = "sdl")]
        let l1 = Sprite::new(24, 2, HENG, ZONG);
        s.add_by_tag(l1, "grid1");

        #[cfg(not(feature = "sdl"))]
        let l2 = Sprite::new(27, 8, 8, 4);
        #[cfg(feature = "sdl")]
        let l2 = Sprite::new(13, 8, 4, 4);
        s.add_by_tag(l2, "next");

        #[cfg(not(feature = "sdl"))]
        let l3 = Sprite::new(42, 8, 8, 4);
        #[cfg(feature = "sdl")]
        let l3 = Sprite::new(17, 8, 4, 4);
        s.add_by_tag(l3, "hold");

        event_register("Tetris.RedrawNext", "redraw_next");
        event_register("Tetris.RedrawHold", "redraw_hold");
        event_register("Tetris.RedrawMsg", "redraw_msg");

        Self {
            term: t,
            main_scene: s,
        }
    }

    fn set_block(&mut self, sname: &str, x: u16, y: u16, c: u8) {
        let cv = vec![
            Color::Magenta,
            Color::Cyan,
            Color::LightRed,
            Color::LightGreen,
            Color::LightBlue,
            Color::LightYellow,
            Color::LightMagenta,
            Color::LightCyan,
        ];
        let c1: &str;
        #[cfg(not(feature = "sdl"))]
        let c2: &str;
        let fg: Color;
        let bg: Color;
        let l = self.main_scene.get_by_tag(sname);

        #[cfg(feature = "sdl")]
        match c {
            0 => {
                c1 = " ";
                fg = Color::Reset;
                bg = Color::Reset;
            }
            11 => {
                c1 = sdlsym(160);
                fg = Color::Indexed(240);
                bg = Color::Reset;
            }
            20 => {
                c1 = sdlsym(102);
                fg = Color::Indexed(242);
                bg = Color::Red;
            }
            30 => {
                c1 = sdlsym(83);
                fg = Color::Indexed(231);
                bg = Color::Red;
            }
            _ => {
                c1 = sdlsym(207);
                fg = cv[(c % 100) as usize % cv.len()];
                bg = Color::Red;
            }
        }

        #[cfg(not(feature = "sdl"))]
        match c {
            0 => {
                c1 = " ";
                c2 = " ";
                fg = Color::Reset;
                bg = Color::Reset;
            }
            11 => {
                c1 = "█";
                c2 = "█";
                fg = Color::Indexed(240);
                bg = Color::Reset;
            }
            20 => {
                c1 = "░";
                c2 = "░";
                fg = Color::Indexed(242);
                bg = Color::Reset;
            }
            30 => {
                c1 = "-";
                c2 = "=";
                fg = Color::Indexed(231);
                bg = Color::Reset;
            }
            _ => {
                c1 = "[";
                c2 = "]";
                fg = cv[(c % 100) as usize % cv.len()];
                bg = Color::Reset;
            }
        }

        #[cfg(feature = "sdl")]
        if x < HENG && y < ZONG {
            l.content.set_str(x, y, c1, Style::default().fg(fg).bg(bg));
        }
        #[cfg(not(feature = "sdl"))]
        if x < HENG * 2 && y < ZONG {
            l.content.set_str(x, y, c1, Style::default().fg(fg).bg(bg));
        }

        #[cfg(not(feature = "sdl"))]
        if x + 1 < HENG * 2 && y < ZONG {
            l.content
                .set_str(x + 1, y, c2, Style::default().fg(fg).bg(bg));
        }
    }

    pub fn redraw_hold<G: Model>(&mut self, model: &mut G) {
        let d = model.as_any().downcast_mut::<TetrisModel>().unwrap();
        for i in 0..4 {
            for j in 0..4 {
                #[cfg(not(feature = "sdl"))]
                let rx = j * 2;
                #[cfg(feature = "sdl")]
                let rx = j;
                if d.cells[0].get_md(d.cells[0].core.save_block, 0, i * 4 + j) != 0 {
                    self.set_block(
                        "hold",
                        rx as u16,
                        i as u16,
                        d.cells[0].core.save_block as u8 + 1,
                    );
                } else {
                    self.set_block("hold", rx as u16, i as u16, 0);
                }
            }
        }
    }

    pub fn redraw_next<G: Model>(&mut self, model: &mut G) {
        let d = model.as_any().downcast_mut::<TetrisModel>().unwrap();
        for i in 0..4 {
            for j in 0..4 {
                #[cfg(not(feature = "sdl"))]
                let rx = j * 2;
                #[cfg(feature = "sdl")]
                let rx = j;
                if d.cells[0].get_md(d.cells[0].core.next_block, 0, i * 4 + j) != 0 {
                    self.set_block(
                        "next",
                        rx as u16,
                        i as u16,
                        d.cells[0].core.next_block as u8 + 1,
                    );
                } else {
                    self.set_block("next", rx as u16, i as u16, 0);
                }
            }
        }
    }

    pub fn draw_grid<G: Model>(&mut self, _context: &mut Context, model: &mut G) {
        let d = model.as_any().downcast_mut::<TetrisModel>().unwrap();
        for n in 0..2 {
            let frs = timer_stage(&format!("clear-row{}", n));
            let mut fri: Vec<i8> = vec![];
            if frs != 0 {
                let fr = timer_exdata(&format!("clear-row{}", n)).unwrap();
                fri = bincode::deserialize(&fr).unwrap();
                //info!("frs..{} fri..{:?}", frs, fri);
            }
            for i in 0..ZONG {
                for j in 0..HENG {
                    #[cfg(not(feature = "sdl"))]
                    let rx = j * 2;
                    #[cfg(feature = "sdl")]
                    let rx = j;
                    let gv = d.cells[n].get_gd(i as i8, (j + 2) as i8);
                    match gv {
                        0 => {
                            self.set_block(&format!("grid{}", n), rx, i, 0);
                        }
                        _ => {
                            let mut hidden_fullrow = false;
                            if frs != 0 {
                                if fri.contains(&(i as i8)) && frs / 3 % 2 == 0 {
                                    hidden_fullrow = true;
                                }
                            }
                            if hidden_fullrow {
                                self.set_block(&format!("grid{}", n), rx, i, 30);
                            } else {
                                self.set_block(&format!("grid{}", n), rx, i, gv % 100);
                            }
                        }
                    }
                }
            }
            for i in 0..4 {
                for j in 0..4 {
                    let ttx = d.cells[n].core.shadow_x + j;
                    let tty = d.cells[n].core.shadow_y + i;
                    if d.cells[n].is_in_grid(tty, ttx) {
                        if d.cells[n].get_md(
                            d.cells[n].core.cur_block,
                            d.cells[n].core.cur_z,
                            i * 4 + j,
                        ) != 0
                        {
                            #[cfg(not(feature = "sdl"))]
                            let rx = ttx * 2 - 4;
                            #[cfg(feature = "sdl")]
                            let rx = ttx - 2;
                            //确保阴影和正常块重合时，阴影不遮盖正常块
                            if d.cells[n].get_gd(tty, ttx) == 0 {
                                self.set_block(
                                    &format!("grid{}", n),
                                    rx as u16,
                                    tty as u16,
                                    20,
                                );
                            }
                        }
                    }
                }
            }
        }
    }
}

impl Render for TetrisRender {
    fn init(&mut self, context: &mut Context) {
        #[cfg(feature = "sdl")]
        context.adapter.init(35, 24, 0.5, 0.5, "tetris".to_string());
        #[cfg(not(feature = "sdl"))]
        context.adapter.init(80, 30, 1.0, 1.0, "tetris".to_string());
        self.term.init(context);
    }

    fn draw<G: Model>(&mut self, context: &mut Context, data: &mut G, _dt: f32) {
        self.draw_grid(context, data);
        self.term
            .draw(context, |f| {
                let sname = ["back", "grid0", "grid1", "next", "hold"];
                for sn in &sname {
                    f.render_widget(self.main_scene.get_by_tag(*sn));
                }
            })
            .unwrap();
    }

    fn handle_event<G: Model>(&mut self, _context: &mut Context, data: &mut G, _dt: f32) {
        if event_check("Tetris.RedrawNext", "redraw_next") {
            self.redraw_next(data);
        }
        if event_check("Tetris.RedrawHold", "redraw_hold") {
            self.redraw_hold(data);
        }
    }

    fn handle_timer<G: Model>(&mut self, _context: &mut Context, _data: &mut G, _dt: f32) {}
}
